package com.imooc.pan.core.utils;

import cn.hutool.core.date.DateUtil;
import com.imooc.pan.core.constants.RPanConstants;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.util.CollectionUtils;

import java.io.*;
import java.nio.channels.*;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import java.util.List;
import java.util.Objects;
import java.util.UUID;

/**
 * 文件工具类
 * @author hhb
 * @data 2024/6/8 21:29
 */
public class FileUtil {
    /**
     * 获取文件后缀名
     * @param filename
     * @return
     */
    public static String getFileSuffix(String filename){
        if(StringUtils.isBlank(filename) || filename.lastIndexOf(RPanConstants.POINT_STR) == RPanConstants.MINUS_ONE_INT){
            return RPanConstants.EMPTY_STR;
        }
        return filename.substring(filename.lastIndexOf(RPanConstants.POINT_STR)).toLowerCase();
    }

    /**
     * 描述文件功能
     * @param totalSize
     * @return
     */
    public static String byteCountToDisplayFile(Long totalSize) {
        if(Objects.isNull(totalSize)){
            return RPanConstants.EMPTY_STR;
        }
        return FileUtils.byteCountToDisplaySize(totalSize);
    }

    /**
     * 删除文件
     * @param realPathList
     */
    public static void deleteFile(List<String> realPathList) throws IOException {
        if(CollectionUtils.isEmpty(realPathList)){
            return;
        }
        for (String realPath : realPathList) {
            FileUtils.forceDelete(new File(realPath));
        }
    }

    /**
     * 生成文件名称
     * 基础路径+年月日+随机编号
     * @param basePath
     * @param filename
     * @return
     */
    public static String generateStoreFileRealPath(String basePath, String filename) {
        return new StringBuffer()
                .append(basePath)
                .append(File.separator)
                .append(DateUtil.thisYear())
                .append(File.separator)
                .append(DateUtil.thisMonth() + 1)
                .append(File.separator)
                .append(DateUtil.thisDayOfMonth())
                .append(File.separator)
                .append(UUIDUtil.getUUID())
                .append(getFileSuffix(filename))
                .toString();
    }

    /**
     * 将文件的输入流写入到文件总
     * 使用底层的sendFile零拷贝来提高传输效率
     * @param inputStream
     * @param targetFile
     * @param totalSize
     */
    public static void writeStreamToFile(InputStream inputStream, File targetFile, Long totalSize) throws IOException{
        createFile(targetFile);
        RandomAccessFile randomAccessFile = new RandomAccessFile(targetFile,"rw");
        FileChannel outPutChannel = randomAccessFile.getChannel();
        ReadableByteChannel inputChannel = Channels.newChannel(inputStream);
        outPutChannel.transferFrom(inputChannel,0L,totalSize);
        randomAccessFile.close();
        inputChannel.close();
        outPutChannel.close();

    }

    /**
     * 创建文件
     * 包含父文件一起去创建
     * @param targetFile
     */
    public static void createFile(File targetFile) throws IOException{
        if(!targetFile.getParentFile().exists()){
            targetFile.getParentFile().mkdirs();
        }
        targetFile.createNewFile();
    }

    /**
     * 生成默认的文件存储路径
     * 生成规则：当前用户的文件目录 + rpan
     * @return
     */
    public static String generateDefaultStoreFileRealPath() {
        return new StringBuffer().append("D:\\")
                .append(File.separator)
                .append("rpan")
                .toString();
    }

    /**
     * 生成默认的文件分片存储路径
     * 生成规则：当前用户的文件目录 + rpan
     * @return
     */
    public static String generateDefaultStoreFileChunkRealPath() {
        return new StringBuffer("D:\\")
                .append(File.separator)
                .append("rpan")
                .append(File.separator)
                .append("chunks")
                .toString();
    }

    /**
     * 生成文件分片的存储路径
     * <p>
     * 生成规则：基础路径 + 年 + 月 + 日 + 唯一标识 + 随机的文件名称 + __,__ + 文件分片的下标
     *
     * @param basePath
     * @param identifier
     * @param chunkNumber
     * @return
     */
    public static String generateStoreFileChunkRealPath(String basePath, String identifier, Integer chunkNumber) {
        return new StringBuffer(basePath)
                .append(File.separator)
                .append(DateUtil.thisYear())
                .append(File.separator)
                .append(DateUtil.thisMonth() + 1)
                .append(File.separator)
                .append(DateUtil.thisDayOfMonth())
                .append(File.separator)
                .append(identifier)
                .append(File.separator)
                .append(UUIDUtil.getUUID())
                .append(RPanConstants.COMMON_SEPARATOR)
                .append(chunkNumber)
                .toString();
    }

    /**
     * 追加写文件
     * @param target
     * @param source
     */
    public static void appendWrite(Path target, Path source) throws IOException{
        Files.write(target,Files.readAllBytes(source), StandardOpenOption.APPEND);
    }

    /**
     * 利用零拷贝技术读取文件内容并写入到文件的输出流中
     * @param fileInputStream
     * @param outputStream
     * @param length
     */
    public static void writeFileToOutputStream(FileInputStream fileInputStream, OutputStream outputStream, long length) throws IOException{
        FileChannel fileChannel = fileInputStream.getChannel();
        WritableByteChannel writableByteChannel = Channels.newChannel(outputStream);
        fileChannel.transferTo(RPanConstants.ZERO_LONG,length,writableByteChannel);
        outputStream.flush();
        fileInputStream.close();
        fileChannel.close();
        outputStream.close();
        writableByteChannel.close();
    }

    /**
     * 普通的流对流数据传输
     * @param inputStream
     * @param outputStream
     */
    public static void writeStreamTOStreamNormal(InputStream inputStream, OutputStream outputStream) throws IOException{
        byte[] buffer = new byte[1024];
        int len;
        while((len = inputStream.read(buffer)) != RPanConstants.MINUS_ONE_INT){
            outputStream.write(buffer,RPanConstants.ZERO_INT,len);
        }
        outputStream.flush();
        outputStream.close();
        inputStream.close();
    }
}
